/**
 * 
 */
package fekih.ece.ing4.monte.carlo.multithread;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import fekih.ece.ing4.monte.carlo.singlethread.AbstractMonteCarlo;

/**
 * @author YacineFekih
 * 
 */
public class ConcurrentMonteCarlo extends AbstractMonteCarlo {

	public double computeMonteCarlo(String[] paramsMT)
			throws InterruptedException, ExecutionException {

		final int numberOfCores = Runtime.getRuntime().availableProcessors();
		final double blockingCoefficient = 0.9;
		final int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));

		System.out.println("Number of Cores available is " + numberOfCores);
		System.out.println("Pool size is " + poolSize);
		final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();

		String callPutFlag = paramsMT[0];
		final double s = Double.valueOf(paramsMT[1]);
		double x = Double.valueOf(paramsMT[2]);
		double t = Double.valueOf(paramsMT[3]);
		double r = Double.valueOf(paramsMT[4]);
		double b = Double.valueOf(paramsMT[5]);
		double v = Double.valueOf(paramsMT[6]);
		final int nSteps = Integer.valueOf(paramsMT[7]);
		int nSimulations = Integer.valueOf(paramsMT[8]);
		
		
		double dt;
		final double drift;
		final double vSqrdt;
		int z= 0;
		
		dt = t / nSteps;
		drift = (b - (v*v)/2)*dt;
		vSqrdt = v * Math.sqrt(dt);
		
		if (callPutFlag.equals("c"))
			z = 1;
		else if (callPutFlag.equals("p"))
			z = -1;
		
		for (int i = 1; i <= nSimulations; i++) {
			partitions.add(new Callable<Double>() {
				Random random = new Random();
				double st =s;
				public Double call() throws Exception {
					for (int j = 1; j<=nSteps; j++){
						st= st * Math.exp(drift + (vSqrdt * random.nextGaussian()));
					}
					return st;
				}
			});
		}

		final ExecutorService executorPool = Executors
				.newFixedThreadPool(poolSize);
		final List<Future<Double>> valueOfEachStep = executorPool.invokeAll(
				partitions, 10000, TimeUnit.SECONDS);

		double sum = 0.0;
		for (final Future<Double> valueOfAStep : valueOfEachStep)
			sum = sum + Math.max(z*(valueOfAStep.get()-x), 0);

		executorPool.shutdown();
		double rt = r*t;
		double res = Math.exp(-rt)*(sum/nSimulations); 
		return res;
	}

	public static void main(final String[] args) throws ExecutionException,
			InterruptedException, IOException {
		new ConcurrentMonteCarlo().timeAndComputeValue();
	}

}
